package com.deepwiki.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Stream;

/**
 * 代码分析服务
 */
@Service
public class CodeAnalysisService {

    private static final Logger logger = LoggerFactory.getLogger(CodeAnalysisService.class);

    // 支持的代码文件扩展名
    private static final Set<String> CODE_EXTENSIONS = Set.of(
            ".java", ".js", ".ts", ".tsx", ".jsx", ".py", ".cs", ".cpp", ".c", ".h", ".hpp",
            ".go", ".rs", ".php", ".rb", ".kt", ".scala", ".swift", ".dart", ".vue", ".svelte"
    );

    // 配置文件扩展名
    private static final Set<String> CONFIG_EXTENSIONS = Set.of(
            ".yml", ".yaml", ".json", ".xml", ".toml", ".ini", ".cfg", ".conf", ".properties"
    );

    // 文档文件扩展名
    private static final Set<String> DOC_EXTENSIONS = Set.of(
            ".md", ".txt", ".rst", ".adoc"
    );

    // 需要忽略的目录
    private static final Set<String> IGNORED_DIRECTORIES = Set.of(
            ".git", ".svn", ".hg", "node_modules", ".gradle", ".maven", "target", "build",
            "dist", "out", ".idea", ".vscode", "__pycache__", ".pytest_cache", ".coverage",
            "bin", "obj", "packages", ".nuget"
    );

    /**
     * 分析代码仓库，生成目录结构
     *
     * @param repositoryPath 仓库路径
     * @return 目录结构字符串
     */
    public String analyzeRepositoryStructure(String repositoryPath) {
        try {
            logger.info("开始分析仓库结构: {}", repositoryPath);
            
            Path repoPath = Paths.get(repositoryPath);
            if (!Files.exists(repoPath)) {
                throw new IllegalArgumentException("仓库路径不存在: " + repositoryPath);
            }

            StringBuilder structure = new StringBuilder();
            structure.append("# Repository Structure Analysis\n\n");
            
            // 分析项目基本信息
            analyzeProjectInfo(repoPath, structure);
            
            // 分析目录结构
            structure.append("## Directory Structure\n\n");
            structure.append("```\n");
            generateDirectoryTree(repoPath, structure, "", 0, 3); // 限制深度为3层
            structure.append("```\n\n");
            
            // 分析主要文件
            analyzeMainFiles(repoPath, structure);
            
            // 分析代码统计
            analyzeCodeStatistics(repoPath, structure);
            
            logger.info("仓库结构分析完成: {}", repositoryPath);
            return structure.toString();
            
        } catch (Exception e) {
            logger.error("分析仓库结构失败: {}", repositoryPath, e);
            throw new RuntimeException("分析仓库结构失败: " + e.getMessage(), e);
        }
    }

    /**
     * 获取代码文件内容（用于AI分析）
     *
     * @param repositoryPath 仓库路径
     * @param maxFiles 最大文件数
     * @param maxSizePerFile 每个文件的最大大小（字节）
     * @return 代码文件内容
     */
    public String getCodeFilesContent(String repositoryPath, int maxFiles, long maxSizePerFile) {
        try {
            logger.info("开始提取代码文件内容: {}, maxFiles: {}, maxSize: {}", repositoryPath, maxFiles, maxSizePerFile);
            
            Path repoPath = Paths.get(repositoryPath);
            StringBuilder content = new StringBuilder();
            
            List<Path> codeFiles = findCodeFiles(repoPath, maxFiles);
            
            for (Path file : codeFiles) {
                try {
                    if (Files.size(file) > maxSizePerFile) {
                        continue; // 跳过过大的文件
                    }
                    
                    String relativePath = repoPath.relativize(file).toString();
                    String fileContent = Files.readString(file, StandardCharsets.UTF_8);
                    
                    content.append("## File: ").append(relativePath).append("\n\n");
                    content.append("```").append(getFileExtension(file)).append("\n");
                    content.append(fileContent);
                    content.append("\n```\n\n");
                    
                } catch (Exception e) {
                    logger.warn("读取文件失败: {}", file, e);
                }
            }
            
            logger.info("代码文件内容提取完成，共处理 {} 个文件", codeFiles.size());
            return content.toString();
            
        } catch (Exception e) {
            logger.error("提取代码文件内容失败: {}", repositoryPath, e);
            throw new RuntimeException("提取代码文件内容失败: " + e.getMessage(), e);
        }
    }

    /**
     * 分析项目基本信息
     */
    private void analyzeProjectInfo(Path repoPath, StringBuilder structure) throws IOException {
        structure.append("## Project Information\n\n");
        
        // 查找README文件
        Optional<Path> readmeFile = findReadmeFile(repoPath);
        if (readmeFile.isPresent()) {
            try {
                String readmeContent = Files.readString(readmeFile.get(), StandardCharsets.UTF_8);
                structure.append("### README Content (First 500 characters)\n\n");
                String preview = readmeContent.length() > 500 ? 
                    readmeContent.substring(0, 500) + "..." : readmeContent;
                structure.append("```\n").append(preview).append("\n```\n\n");
            } catch (Exception e) {
                logger.warn("读取README文件失败", e);
            }
        }
        
        // 分析项目类型
        String projectType = detectProjectType(repoPath);
        structure.append("### Project Type\n\n");
        structure.append(projectType).append("\n\n");
    }

    /**
     * 分析主要文件
     */
    private void analyzeMainFiles(Path repoPath, StringBuilder structure) throws IOException {
        structure.append("## Key Files\n\n");
        
        try (Stream<Path> files = Files.walk(repoPath, 2)) {
            files.filter(Files::isRegularFile)
                 .filter(this::isKeyFile)
                 .forEach(file -> {
                     String relativePath = repoPath.relativize(file).toString();
                     structure.append("- ").append(relativePath).append("\n");
                 });
        }
        structure.append("\n");
    }

    /**
     * 分析代码统计
     */
    private void analyzeCodeStatistics(Path repoPath, StringBuilder structure) throws IOException {
        structure.append("## Code Statistics\n\n");
        
        Map<String, Integer> fileCount = new HashMap<>();
        Map<String, Long> lineCount = new HashMap<>();
        
        try (Stream<Path> files = Files.walk(repoPath)) {
            files.filter(Files::isRegularFile)
                 .filter(this::isCodeFile)
                 .forEach(file -> {
                     String ext = getFileExtension(file);
                     fileCount.merge(ext, 1, Integer::sum);
                     
                     try {
                         long lines = Files.lines(file).count();
                         lineCount.merge(ext, lines, Long::sum);
                     } catch (Exception e) {
                         logger.warn("统计文件行数失败: {}", file, e);
                     }
                 });
        }
        
        structure.append("| Language | Files | Lines |\n");
        structure.append("|----------|-------|-------|\n");
        fileCount.entrySet().stream()
                 .sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
                 .forEach(entry -> {
                     String ext = entry.getKey();
                     Integer files = entry.getValue();
                     Long lines = lineCount.getOrDefault(ext, 0L);
                     structure.append("| ").append(ext).append(" | ")
                             .append(files).append(" | ")
                             .append(lines).append(" |\n");
                 });
        structure.append("\n");
    }

    /**
     * 生成目录树
     */
    private void generateDirectoryTree(Path path, StringBuilder structure, String prefix, int depth, int maxDepth) throws IOException {
        if (depth > maxDepth) {
            return;
        }
        
        String fileName = path.getFileName() != null ? path.getFileName().toString() : path.toString();
        structure.append(prefix).append(fileName).append("\n");
        
        if (Files.isDirectory(path) && !isIgnoredDirectory(fileName)) {
            try (Stream<Path> children = Files.list(path)) {
                List<Path> sortedChildren = children.sorted((a, b) -> {
                    boolean aIsDir = Files.isDirectory(a);
                    boolean bIsDir = Files.isDirectory(b);
                    if (aIsDir != bIsDir) {
                        return aIsDir ? -1 : 1; // 目录在前
                    }
                    return a.getFileName().toString().compareTo(b.getFileName().toString());
                }).toList();
                
                for (int i = 0; i < sortedChildren.size() && i < 20; i++) { // 限制每层最多20个项目
                    Path child = sortedChildren.get(i);
                    boolean isLast = i == sortedChildren.size() - 1;
                    String childPrefix = prefix + (isLast ? "└── " : "├── ");
                    String nextPrefix = prefix + (isLast ? "    " : "│   ");
                    
                    generateDirectoryTree(child, structure, childPrefix, depth + 1, maxDepth);
                }
                
                if (sortedChildren.size() > 20) {
                    structure.append(prefix).append("└── ... (").append(sortedChildren.size() - 20).append(" more items)\n");
                }
            }
        }
    }

    /**
     * 查找代码文件
     */
    private List<Path> findCodeFiles(Path repoPath, int maxFiles) throws IOException {
        List<Path> codeFiles = new ArrayList<>();
        
        try (Stream<Path> files = Files.walk(repoPath)) {
            files.filter(Files::isRegularFile)
                 .filter(this::isCodeFile)
                 .filter(file -> !isInIgnoredDirectory(repoPath, file))
                 .sorted((a, b) -> {
                     // 优先选择重要文件
                     int priorityA = getFilePriority(a);
                     int priorityB = getFilePriority(b);
                     if (priorityA != priorityB) {
                         return Integer.compare(priorityB, priorityA); // 高优先级在前
                     }
                     return a.toString().compareTo(b.toString());
                 })
                 .limit(maxFiles)
                 .forEach(codeFiles::add);
        }
        
        return codeFiles;
    }

    /**
     * 获取文件优先级
     */
    private int getFilePriority(Path file) {
        String fileName = file.getFileName().toString().toLowerCase();
        String ext = getFileExtension(file);
        
        // 配置文件优先级最高
        if (isKeyFile(file)) {
            return 10;
        }
        
        // 入口文件
        if (fileName.contains("main") || fileName.contains("index") || fileName.contains("app")) {
            return 8;
        }
        
        // 特定语言的主要文件
        if (ext.equals("java") && fileName.contains("application")) {
            return 7;
        }
        
        // 普通代码文件
        if (CODE_EXTENSIONS.contains("." + ext)) {
            return 5;
        }
        
        return 1;
    }

    /**
     * 检测项目类型
     */
    private String detectProjectType(Path repoPath) {
        List<String> types = new ArrayList<>();
        
        try (Stream<Path> files = Files.list(repoPath)) {
            Set<String> fileNames = new HashSet<>();
            files.forEach(file -> fileNames.add(file.getFileName().toString().toLowerCase()));
            
            if (fileNames.contains("pom.xml")) types.add("Maven Java Project");
            if (fileNames.contains("build.gradle")) types.add("Gradle Project");
            if (fileNames.contains("package.json")) types.add("Node.js Project");
            if (fileNames.contains("requirements.txt") || fileNames.contains("setup.py")) types.add("Python Project");
            if (fileNames.contains("cargo.toml")) types.add("Rust Project");
            if (fileNames.contains("go.mod")) types.add("Go Project");
            if (fileNames.contains("composer.json")) types.add("PHP Project");
            if (fileNames.contains("gemfile")) types.add("Ruby Project");
            
        } catch (IOException e) {
            logger.warn("检测项目类型失败", e);
        }
        
        return types.isEmpty() ? "Unknown Project Type" : String.join(", ", types);
    }

    /**
     * 查找README文件
     */
    private Optional<Path> findReadmeFile(Path repoPath) {
        try (Stream<Path> files = Files.list(repoPath)) {
            return files.filter(Files::isRegularFile)
                       .filter(file -> {
                           String name = file.getFileName().toString().toLowerCase();
                           return name.startsWith("readme");
                       })
                       .findFirst();
        } catch (IOException e) {
            return Optional.empty();
        }
    }

    /**
     * 是否为代码文件
     */
    private boolean isCodeFile(Path file) {
        String ext = "." + getFileExtension(file);
        return CODE_EXTENSIONS.contains(ext) || CONFIG_EXTENSIONS.contains(ext) || DOC_EXTENSIONS.contains(ext);
    }

    /**
     * 是否为关键文件
     */
    private boolean isKeyFile(Path file) {
        String fileName = file.getFileName().toString().toLowerCase();
        return fileName.equals("pom.xml") || fileName.equals("build.gradle") || 
               fileName.equals("package.json") || fileName.equals("requirements.txt") ||
               fileName.equals("dockerfile") || fileName.equals("docker-compose.yml") ||
               fileName.startsWith("readme") || fileName.equals("license") ||
               fileName.equals("makefile") || fileName.equals("cargo.toml") ||
               fileName.equals("go.mod") || fileName.equals("composer.json");
    }

    /**
     * 是否为忽略目录
     */
    private boolean isIgnoredDirectory(String dirName) {
        return IGNORED_DIRECTORIES.contains(dirName.toLowerCase()) || dirName.startsWith(".");
    }

    /**
     * 是否在忽略目录中
     */
    private boolean isInIgnoredDirectory(Path repoPath, Path file) {
        Path relativePath = repoPath.relativize(file);
        for (Path part : relativePath) {
            if (isIgnoredDirectory(part.toString())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取文件扩展名
     */
    private String getFileExtension(Path file) {
        String fileName = file.getFileName().toString();
        int dotIndex = fileName.lastIndexOf('.');
        return dotIndex > 0 ? fileName.substring(dotIndex + 1).toLowerCase() : "";
    }
} 